package com.mingqijia.gassafety.authority.security.provider;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.gap.cache.redis.RedisCache;
import com.mingqijia.gassafety.authority.api.req.misc.Message;
import com.mingqijia.gassafety.authority.api.resp.resource.ResourceLiteRespDTO;
import com.mingqijia.gassafety.authority.dal.helper.ConfigureHolder;
import com.mingqijia.gassafety.authority.dal.provider.CompanyProvider;
import com.mingqijia.gassafety.authority.dal.provider.ManagerProvider;
import com.mingqijia.gassafety.authority.dal.provider.PrivilegeProvider;
import com.mingqijia.gassafety.authority.dal.provider.ResourceProvider;
import com.mingqijia.gassafety.authority.security.valueobject.ManagerVO;
import com.mingqijia.gassafety.db.entity.auth.CompanyEntry;
import com.mingqijia.gassafety.db.entity.auth.ManagerEntry;
import com.mingqijia.gassafety.db.entity.auth.PrivilegeEntry;
import com.mingqijia.gassafety.db.entity.auth.ResourceEntry;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.cqrs.MessageEvent;
import com.mingqijia.gassafety.shared.enums.RunningState;
import com.mingqijia.gassafety.shared.enums.SmsType;
import com.mingqijia.gassafety.shared.enums.YesNoEnum;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.utils.MappingTools;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 工具类
 * @author Damon S.
 * @version v0.2
 * @date 2022-02-27 11:04
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthorityProvider {

    @Value("${spring.profiles.active:dev}")
    private String environment;

    private final ResourceProvider rProvider;
    private final PrivilegeProvider pProvider;
    private final ConfigureHolder holder;
    private final RedisCache cacheManager;
    private final CompanyProvider cProvider;
    private final ManagerProvider mProvider;

    private final static String CAPTCHA_KEY = "captcha_type";
    private final static String CAPTCHA_ENABLED = "captcha_enabled";
    private final static String CAPTCHA_SMS = "sms";
    private final static String CAPTCHA_IMAGE = "image";

    /** 电话号码验证 */
    private final static Pattern REGEXP = Pattern.compile(Constants.REGEXP_PHONE);

    public String createCaptcha(String username, HttpServletResponse response) {
        String captchaType = holder.getConfigByKey(CAPTCHA_KEY);
        if (CAPTCHA_IMAGE.equalsIgnoreCase(captchaType)) {
            // 图片验证码
            try {
                ApplicationUtils.writeOutImage(
                        response,
                        createCaptchaImage(username),
                        Constants.IMAGE_EXT_JPG
                );
            } catch (Exception e) {
                log.error("生成图片验证码失败", e);
            }
        } else if (CAPTCHA_SMS.equalsIgnoreCase(captchaType)) {
            ManagerEntry entry = mProvider.getByUsername(username);
            if (ObjectUtils.isEmpty(entry)) {
                throw new GlobalAuthenticationException("账号不存在");
            }
            // 生成短信验证码
            String smsCode = createCaptchaSms(username, entry.getPhone());
            // 对接短消息接口
            if (StringUtils.hasText(smsCode)) {
                Message message = Message.builder().type(SmsType.LOGIN)
                        .code(smsCode).phone(entry.getPhone()).build();
                ApplicationUtils.CTX.publishEvent(new MessageEvent(this, message));
            }
        }
        return Constants.STR_EMPTY;
    }

    /**
     * 判断验证码是否一致
     * @param username 登录信息
     * @return 返回验证码是否一致
     */
    public boolean checkCaptcha(String username, String captcha) {
        String captchaEnabled = holder.getConfigByKey(CAPTCHA_ENABLED);
        // 非生产环境，且配置生效则直接返回通过验证
        if (!Constants.ENV_PROD.contains(environment)
                && !YesNoEnum.parse(captchaEnabled).isFlag()) {
            return false;
        }

        String key = String.format(
                Constants.KEY_MANAGER_CAPTCHA, username
        );
        String cachedCaptcha = cacheManager.getCacheObject(key);

        // 判断验证码是否匹配
        return !Objects.equals(captcha, cachedCaptcha);
    }

    public List<CompanyEntry> getAuthorizedSp() {
        ManagerVO backend = (ManagerVO) ApplicationUtils.getWorkingUser();
        // 企业级账号返回所在企业
        if (CollectionUtils.isEmpty(backend.getHierarchy())) {
            return Collections.singletonList(cProvider.getById(backend.getSpId()));
        }
        // 当 hierarchy 设置未到企业/子公司一级时
        // 给杭州使用
//        List<CompanyEntry> entries = Arrays.asList(cProvider.getById(
//                backend.getHierarchy().get(backend.getHierarchy().size() - Constants.INT_ONE)
//        ));
//        Set<CompanyEntry> authorizedSps = new HashSet<>(entries);

        // 平台使用
        String currentCompanyId = backend.getHierarchy().get(
                backend.getHierarchy().size() - Constants.INT_ONE
        );
        List<CompanyEntry> entries = Arrays.asList(cProvider.getById(currentCompanyId));
        Set<CompanyEntry> authorizedSps = new HashSet<>(entries);
        authorizedSps.addAll(cProvider.findByParentIdHierarchy(currentCompanyId));
//        entries.forEach(entry -> {
//            if (Objects.equals(Constants.PLATFORM_SP, entry.getId())) {
//                return;
//            }
//            authorizedSps.addAll(cProvider.findByParentIdHierarchy(entry.getId()));
//        });

        // 返回公司、子公司和平台
        return authorizedSps.stream().filter(item ->
                Objects.equals(RunningState.RUNNING.getCode(), item.getState())
                && Objects.equals(YesNoEnum.N.isFlag(), item.getIsDeleted())
                        && (Objects.equals(item.getType(), Constants.DICTIONARY_PLATFORM)
                        || Objects.equals(item.getType(), Constants.DICTIONARY_SUBSIDIARY)
                        || Objects.equals(item.getType(), Constants.DICTIONARY_ENTERPRISE))
        ).collect(Collectors.toList());
    }

    public List<ResourceLiteRespDTO> getAuthorizedRes(String spId) {
        ManagerVO backend = (ManagerVO) ApplicationUtils.getWorkingUser();
        // 当前用户在此企业下的权限
        List<PrivilegeEntry> entries = pProvider.findByRoleIds(backend.getRoleIds());
        if (CollectionUtils.isEmpty(entries)) {
            return Collections.emptyList();
        }

        // 转换为菜单的资源权限
        Set<String> authorizedResIds = new HashSet<>();
        for (PrivilegeEntry entry : entries) {
            authorizedResIds.addAll(MappingTools.fromRaw(entry.getData()));
        }

        // 获取有效资源列表
        List<ResourceEntry> resources = rProvider.listByIds(authorizedResIds);
        if (CollectionUtils.isEmpty(resources)) {
            return Collections.emptyList();
        }
        return rProvider.buildHierarchyFromRoot(resources);
    }

    /** 返回验证码字符串 */
    private byte[] createCaptchaImage(String username) {
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 35);

        String value = captcha.getCode().toLowerCase();
        String key = String.format(Constants.KEY_MANAGER_CAPTCHA, username);

        String expiration = holder.getConfigByKey(Constants.CACHE_KEY_CAPTCHA_EXPIRY);
        if (StringUtils.hasText(expiration)) {
            cacheManager.setCacheObject(key, value, Integer.parseInt(expiration), TimeUnit.MINUTES);
        } else {
            cacheManager.setCacheObject(key, value, Constants.MINUTES_10, TimeUnit.MINUTES);
        }

        return captcha.getImageBytes();
    }

    /** 返回空字符串 */
    private String createCaptchaSms(String username, String phone) {
        if (!REGEXP.matcher(phone).matches()) {
            throw new GlobalAuthenticationException("电话号码不合法");
        }
        String key = String.format(
                Constants.KEY_MANAGER_CAPTCHA, username
        );
        String smsCode = cacheManager.getCacheObject(key);
        if (StringUtils.isEmpty(smsCode)) {
            // 生成验证码
            smsCode = ApplicationUtils.getRandomInt(6);
        }
        log.info("登录验证码[{}={}]", key, smsCode);

        // 生产环境，或配置生效才用缓存
        String captchaEnabled = holder.getConfigByKey(CAPTCHA_ENABLED);
        if (Constants.ENV_PROD.contains(environment)
                || YesNoEnum.parse(captchaEnabled).isFlag()) {
            // 存入缓存备用
            String expiration = holder.getConfigByKey(Constants.CACHE_KEY_CAPTCHA_EXPIRY);
            if (StringUtils.hasText(expiration)) {
                cacheManager.setCacheObject(key, smsCode, Integer.parseInt(expiration), TimeUnit.MINUTES);
            } else {
                cacheManager.setCacheObject(key, smsCode, Constants.MINUTES_10, TimeUnit.MINUTES);
            }
            return smsCode;
        }
        return Constants.STR_EMPTY;
    }
}
